/*
 * npc_lua.c
 */

#include "npc_lua.h"
#include "npc.h"
#include <rowgage.h>
#include <agent/player.h>
#include <controller/listener.h>
#include <gamelogic/event_utils.h>
#include <time.h>
#include <agent/go_unit.h>

static int npc_lua_initialized = 0;
static void * (*npc_lua_stats_get)(int, struct lua_arg_list*, unit*) = NULL;
static lua_State *npc_lua_callback_state = NULL;
static void (*npc_lua_stats_push)(void*) = NULL;

/*
 * Sets the unit creation callback.
 * This callback should create the structure housing the game-specific game
 * stats from a Lua argument list, returned as an anonymous pointer.
 * A pointer to the unit the game stats are being created for is passed to
 * encourage developers to house a pointer to the ROWGAGE unit struct so that
 * both structures contain pointers to each other.
 */
int npc_lua_stats( void * (*get_stats)(int, struct lua_arg_list*, unit*) ) {
    npc_lua_stats_get = get_stats;
    return 0;
}

/*
 * An indirect Lua callback.
 * Creates a new unit from the given Lua argument list. Then, if the callback
 * has been set, calls the stats callback with the remainder of the argument
 * list to create the stats struct.
 */
void npc_lua_create( int argc, struct lua_arg_list *argv ) {
    const char *name;
    int x;
    int y;
    int z;
    unit *u;
    if( argc < 4 ) {
        /* TODO: error */
        printf("Too few arguments in npc_lua_create call!\n");
    }
    else {
        argv = luaR_arg_getinteger(argv, &x);
        argv = luaR_arg_getinteger(argv, &y);
        argv = luaR_arg_getinteger(argv, &z);
        argv = luaR_arg_getstring(argv, &name);
        u = npc_create(x, y, z, name, NULL);
        if( npc_lua_stats_get != NULL ) {
            u->stats = npc_lua_stats_get(argc - 4, argv, u);
        }
        luaR_arg_pushinteger(npc_last_id());
    }
}

/*
 * Sets the unit stats callback.
 * This callback should take the given anonymous struct (which should be the
 * game-specific unit stats struct), retrieves the relevant information from it,
 * and push it onto the Lua stack to be used by the npc_lua_getInfo function.
 * Developers cannot access the Lua stack directly, and must instead use the
 * provided ROWGAGE functions.
 */
int npc_lua_push_callback( void (*stats_push)(void*) ) {
    npc_lua_stats_push = stats_push;
    return 0;
}

/*
 * Pushes the given number with the given key onto the Lua stack to be set in
 * the table returned by npc_lua_getInfo.
 */
int npc_lua_pushnumber( const char *key, double value ) {
    if( npc_lua_callback_state != NULL ) {
        lua_pushstring(npc_lua_callback_state, key);
        lua_pushnumber(npc_lua_callback_state, value);
        lua_settable(npc_lua_callback_state, lua_gettop(npc_lua_callback_state) - 2);
        return 0;
    }
    else {
        /* TODO: error */
        return 1;
    }
}

/*
 * Pushes the given string with the given key onto the Lua stack to be set in
 * the table returned by npc_lua_getInfo.
 */
int npc_lua_pushstring( const char *key, const char *value ) {
    if( npc_lua_callback_state != NULL ) {
        lua_pushstring(npc_lua_callback_state, key);
        lua_pushstring(npc_lua_callback_state, value);
        lua_settable(npc_lua_callback_state, lua_gettop(npc_lua_callback_state) - 2);
        return 0;
    }
    else {
        /* TODO: error */
        return 1;
    }
}

/*
 * Lua stand-alone C function hook.
 * Returns the stats of a given NPC as a table. If -1 is passed, returns the
 * stats for the player.
 * If this table were called "npc" (actual name is irrelevant), then npc would
 * have the following members:
 * npc.x npc.y npc.z npc.size npc.hp npc.name
 */
int npc_lua_getInfo( lua_State *L ) {
    unit *u;
    int index;
    if( lua_gettop(L) < 1 ) {
        lua_pushstring(L, "Too few arguments.");
        lua_error(L);
        return 0;
    }
    else {
        index = (int)lua_tonumber(L, 1);
        if( index == -1 ) {
            u = player_get();
        }
        else if( index >= 0 && index <= npc_last_id() ) {
            u = npc_get(index);
        }
        else {
            lua_pushstring(L, "Nonexistant NPC.");
            lua_error(L);
            return 0;
        }
        lua_newtable(L);
        lua_pushstring(L, "x");
        lua_pushnumber(L, u->x);
        lua_settable(L, lua_gettop(L) - 2);
        lua_pushstring(L, "y");
        lua_pushnumber(L, u->y);
        lua_settable(L, lua_gettop(L) - 2);
        lua_pushstring(L, "z");
        lua_pushnumber(L, u->z);
        lua_settable(L, lua_gettop(L) - 2);
        if( npc_lua_stats_push != NULL ) {
            npc_lua_callback_state = L;
            npc_lua_stats_push(u->stats);
            npc_lua_callback_state = NULL;
        }
        /*
        lua_pushstring(L, "size"); * TODO: more GO dependency *
        lua_pushnumber(L, ((go_stats*)u->stats)->size);
        lua_settable(L, lua_gettop(L) - 2);
        lua_pushstring(L, "hp"); * TODO: more GO dependency *
        lua_pushnumber(L, ((go_stats*)u->stats)->hp);
        lua_settable(L, lua_gettop(L) - 2);
        */
        lua_pushstring(L, "name");
        lua_pushstring(L, u->name);
        lua_settable(L, lua_gettop(L) - 2);
        return 1;
    }
}

/*
 * Lua stand-alone C function hook.
 * Returns the elapsed CPU time in seconds. This function generally has a finer
 * grain than the built-in Lua time functions, and as such will probably be
 * preferred.
 */
int npc_lua_getTime( lua_State *L ) {
    lua_pushnumber(L, (double)clock() / CLOCKS_PER_SEC);
    return 1;
}

/*
 * Calls the Lua function "npcTurn", which goes through each created monster and
 * allows it to take an action.
 */
void npc_lua_round() {
    luaR_fcall("npcTurn");
}

/*
 * Initializes the NPC Lua interface.
 */
int npc_lua_initialize() {
    int status;
    if( !npc_lua_initialized ) {
        luaR_package_create();
        luaR_package_add("info", npc_lua_getInfo);
        luaR_package_add("time", npc_lua_getTime);
        luaR_package_set("RG_npc");
        luaR_callback_register("RG_npc", "create", npc_lua_create);
        luaR_load_file("scripts/npc.lua");
        rgAddTurnCallback(npc_lua_round);
        npc_lua_initialized = 1;
        return 0;
    }
    else {
        /* TODO: warning message */
        return 1;
    }
}
